// *****************************************************************************
// *****************************************************************************
// Copyright 2012 - 2015, Cadence Design Systems
//
// This  file  is  part  of  the  Cadence  LEF/DEF  Open   Source
// Distribution,  Product Version 5.8.
//
// Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
//    implied. See the License for the specific language governing
//    permissions and limitations under the License.
//
// For updates, support, or to become part of the LEF/DEF Community,
// check www.openeda.org for details.
//
//  $Author: dell $
//  $Revision: #1 $
//  $Date: 2020/09/29 $
//  $State:  $
// *****************************************************************************
// *****************************************************************************

#include "lefiViaRule.hpp"

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "lefiDebug.hpp"
#include "lefiKRDefs.hpp"
#include "lex.h"

BEGIN_LEF_PARSER_NAMESPACE

// *****************************************************************************
// lefiViaRuleLayer
// *****************************************************************************

lefiViaRuleLayer::lefiViaRuleLayer()
    : widthMin_(0.0),
      widthMax_(0.0),
      overhang_(0.0),
      metalOverhang_(0.0),
      resistance_(0.0),
      spacingStepX_(0.0),
      spacingStepY_(0.0),
      xl_(0.0),
      yl_(0.0),
      xh_(0.0),
      yh_(0.0)
{
  Init();
}

void lefiViaRuleLayer::Init()
{
  name_ = nullptr;
  overhang1_ = -1;
  overhang2_ = -1;
}

void lefiViaRuleLayer::clearLayerOverhang()
{
  overhang1_ = -1;
  overhang2_ = -1;
}

void lefiViaRuleLayer::setName(const char* name)
{
  int len = strlen(name) + 1;
  if (name_) {
    lefFree(name_);
  }
  name_ = (char*) lefMalloc(len);
  strcpy(name_, CASE(name));
  direction_ = '\0';
  // overhang1_ = -1;    already reset in clearLayerOverhang
  // overhang2_ = -1;
  hasWidth_ = 0;
  hasResistance_ = 0;
  hasOverhang_ = 0;
  hasMetalOverhang_ = 0;
  hasSpacing_ = 0;
  hasRect_ = 0;
}

void lefiViaRuleLayer::Destroy()
{
  if (name_) {
    lefFree(name_);
  }
}

lefiViaRuleLayer::~lefiViaRuleLayer()
{
  // Destroy will be called explicitly
  // so do nothing here.
}

void lefiViaRuleLayer::setHorizontal()
{
  direction_ = 'H';
}

void lefiViaRuleLayer::setVertical()
{
  direction_ = 'V';
}

// 5.5
void lefiViaRuleLayer::setEnclosure(double overhang1, double overhang2)
{
  overhang1_ = overhang1;
  overhang2_ = overhang2;
}

void lefiViaRuleLayer::setWidth(double minW, double maxW)
{
  hasWidth_ = 1;
  widthMin_ = minW;
  widthMax_ = maxW;
}

void lefiViaRuleLayer::setOverhang(double d)
{
  hasOverhang_ = 1;
  overhang_ = d;
}

// 5.6
void lefiViaRuleLayer::setOverhangToEnclosure(double d)
{
  if ((overhang1_ != -1) && (overhang2_ != -1)) {
    return;  // both overhang1_ & overhang2_ are set
  }
  if (overhang1_ == -1) {
    overhang1_ = d;  // set value to overhang1_
  } else {
    overhang2_ = d;  // overhang1_ already set, set to overhang2_
  }
  return;
}

// 5.5
void lefiViaRuleLayer::setMetalOverhang(double d)
{
  hasMetalOverhang_ = 1;
  metalOverhang_ = d;
}

void lefiViaRuleLayer::setResistance(double d)
{
  hasResistance_ = 1;
  resistance_ = d;
}

void lefiViaRuleLayer::setSpacing(double x, double y)
{
  hasSpacing_ = 1;
  spacingStepX_ = x;
  spacingStepY_ = y;
}

void lefiViaRuleLayer::setRect(double xl, double yl, double xh, double yh)
{
  hasRect_ = 1;
  xl_ = xl;
  yl_ = yl;
  xh_ = xh;
  yh_ = yh;
}

int lefiViaRuleLayer::hasRect() const
{
  return hasRect_;
}

int lefiViaRuleLayer::hasDirection() const
{
  return direction_ ? 1 : 0;
}

// 5.5
int lefiViaRuleLayer::hasEnclosure() const
{
  return overhang1_ == -1 ? 0 : 1;
}

int lefiViaRuleLayer::hasWidth() const
{
  return hasWidth_;
}

int lefiViaRuleLayer::hasResistance() const
{
  return hasResistance_;
}

int lefiViaRuleLayer::hasOverhang() const
{
  return hasOverhang_;
}

int lefiViaRuleLayer::hasMetalOverhang() const
{
  return hasMetalOverhang_;
}

int lefiViaRuleLayer::hasSpacing() const
{
  return hasSpacing_;
}

char* lefiViaRuleLayer::name() const
{
  return name_;
}

int lefiViaRuleLayer::isHorizontal() const
{
  return direction_ == 'H' ? 1 : 0;
}

int lefiViaRuleLayer::isVertical() const
{
  return direction_ == 'V' ? 1 : 0;
}

// 5.5
double lefiViaRuleLayer::enclosureOverhang1() const
{
  return overhang1_;
}

// 5.5
double lefiViaRuleLayer::enclosureOverhang2() const
{
  return overhang2_;
}

double lefiViaRuleLayer::widthMin() const
{
  return widthMin_;
}

double lefiViaRuleLayer::widthMax() const
{
  return widthMax_;
}

double lefiViaRuleLayer::overhang() const
{
  return overhang_;
}

double lefiViaRuleLayer::metalOverhang() const
{
  return metalOverhang_;
}

double lefiViaRuleLayer::resistance() const
{
  return resistance_;
}

double lefiViaRuleLayer::spacingStepX() const
{
  return spacingStepX_;
}

double lefiViaRuleLayer::spacingStepY() const
{
  return spacingStepY_;
}

double lefiViaRuleLayer::xl() const
{
  return xl_;
}

double lefiViaRuleLayer::yl() const
{
  return yl_;
}

double lefiViaRuleLayer::xh() const
{
  return xh_;
}

double lefiViaRuleLayer::yh() const
{
  return yh_;
}

void lefiViaRuleLayer::print(FILE* f) const
{
  fprintf(f, "  Layer %s", name_);

  if (isHorizontal()) {
    fprintf(f, " HORIZONTAL");
  }
  if (isVertical()) {
    fprintf(f, " VERTICAL");
  }
  fprintf(f, "\n");

  if (hasWidth()) {
    fprintf(f, "    WIDTH %g %g\n", widthMin(), widthMax());
  }

  if (hasResistance()) {
    fprintf(f, "    RESISTANCE %g\n", resistance());
  }

  if (hasOverhang()) {
    fprintf(f, "    OVERHANG %g\n", overhang());
  }

  if (hasMetalOverhang()) {
    fprintf(f, "    METALOVERHANG %g\n", metalOverhang());
  }

  if (hasSpacing()) {
    fprintf(f, "    SPACING %g %g\n", spacingStepX(), spacingStepY());
  }

  if (hasRect()) {
    fprintf(f, "    RECT %g,%g %g,%g\n", xl(), yl(), xh(), yh());
  }
}

// *****************************************************************************
// lefiViaRule
// *****************************************************************************

lefiViaRule::lefiViaRule()
{
  Init();
}

void lefiViaRule::Init()
{
  nameSize_ = 16;
  name_ = (char*) lefMalloc(16);
  viasAllocated_ = 2;
  vias_ = (char**) lefMalloc(sizeof(char*) * 2);
  layers_[0].Init();
  layers_[1].Init();
  layers_[2].Init();
  numLayers_ = 0;
  numVias_ = 0;
  numProps_ = 0;
  propsAllocated_ = 1;
  names_ = (char**) lefMalloc(sizeof(char*));
  values_ = (char**) lefMalloc(sizeof(char*));
  dvalues_ = (double*) lefMalloc(sizeof(double));
  types_ = (char*) lefMalloc(sizeof(char));
}

void lefiViaRule::clear()
{
  int i;
  hasGenerate_ = 0;
  hasDefault_ = 0;
  for (i = 0; i < numProps_; i++) {
    lefFree(names_[i]);
    lefFree(values_[i]);
    dvalues_[i] = 0;
  }
  numProps_ = 0;
  numLayers_ = 0;
  for (i = 0; i < numVias_; i++) {
    lefFree(vias_[i]);
  }
  numVias_ = 0;
}

void lefiViaRule::clearLayerOverhang()
{
  layers_[0].clearLayerOverhang();
  layers_[1].clearLayerOverhang();
}

void lefiViaRule::setName(const char* name)
{
  int len = strlen(name) + 1;
  if (len > nameSize_) {
    lefFree(name_);
    name_ = (char*) lefMalloc(len);
    nameSize_ = len;
  }
  strcpy(name_, CASE(name));
  clear();
}

void lefiViaRule::Destroy()
{
  clear();
  lefFree(name_);
  lefFree(vias_);
  lefFree(names_);
  lefFree(values_);
  lefFree(dvalues_);
  lefFree(types_);
  layers_[0].Destroy();
  layers_[1].Destroy();
  layers_[2].Destroy();
}

lefiViaRule::~lefiViaRule()
{
  Destroy();
}

void lefiViaRule::setGenerate()
{
  hasGenerate_ = 1;
}

void lefiViaRule::setDefault()
{
  hasDefault_ = 1;
}

void lefiViaRule::addViaName(const char* name)
{
  // Add one of possibly many via names
  int len = strlen(name) + 1;
  if (numVias_ == viasAllocated_) {
    int i;
    char** nn;
    if (viasAllocated_ == 0) {
      viasAllocated_ = 2;
    } else {
      viasAllocated_ *= 2;
    }
    nn = (char**) lefMalloc(sizeof(char*) * viasAllocated_);
    for (i = 0; i < numVias_; i++) {
      nn[i] = vias_[i];
    }
    lefFree(vias_);
    vias_ = nn;
  }
  vias_[numVias_] = (char*) lefMalloc(len);
  strcpy(vias_[numVias_], CASE(name));
  numVias_ += 1;
}

void lefiViaRule::setRect(double xl, double yl, double xh, double yh)
{
  layers_[numLayers_ - 1].setRect(xl, yl, xh, yh);
}

void lefiViaRule::setSpacing(double x, double y)
{
  layers_[numLayers_ - 1].setSpacing(x, y);
}

void lefiViaRule::setWidth(double x, double y)
{
  layers_[numLayers_ - 1].setWidth(x, y);
}

void lefiViaRule::setResistance(double d)
{
  layers_[numLayers_ - 1].setResistance(d);
}

void lefiViaRule::setOverhang(double d)
{
  layers_[numLayers_ - 1].setOverhang(d);
}

// 5.6, try to set value to layers_[0] & layers_[1]
void lefiViaRule::setOverhangToEnclosure(double d)
{
  layers_[0].setOverhangToEnclosure(d);
  layers_[1].setOverhangToEnclosure(d);
}

void lefiViaRule::setMetalOverhang(double d)
{
  layers_[numLayers_ - 1].setMetalOverhang(d);
}

void lefiViaRule::setVertical()
{
  layers_[numLayers_ - 1].setVertical();
}

void lefiViaRule::setHorizontal()
{
  layers_[numLayers_ - 1].setHorizontal();
}

void lefiViaRule::setEnclosure(double overhang1, double overhang2)
{
  layers_[numLayers_ - 1].setEnclosure(overhang1, overhang2);
}

void lefiViaRule::setLayer(const char* name)
{
  if (numLayers_ == 3) {
    lefiError(0, 1430, "ERROR (LEFPARS-1430): too many via rule layers");
    return;
  }
  // This routine sets and creates the active layer.
  layers_[numLayers_].setName(name);
  numLayers_ += 1;
}

int lefiViaRule::hasGenerate() const
{
  return hasGenerate_;
}

int lefiViaRule::hasDefault() const
{
  return hasDefault_;
}

int lefiViaRule::numLayers() const
{
  // There are 2 or 3 layers in a rule.
  // numLayers() tells how many.
  // If a third layer exists then it is the cut layer.
  return numLayers_;
}

lefiViaRuleLayer* lefiViaRule::layer(int index) const
{
  if (index < 0 || index > 2) {
    return nullptr;
  }
  return (lefiViaRuleLayer*) &(layers_[index]);
}

char* lefiViaRule::name() const
{
  return name_;
}

void lefiViaRule::print(FILE* f) const
{
  int i;
  fprintf(f, "VIA RULE %s", name());
  if (hasGenerate()) {
    fprintf(f, " GENERATE");
  }
  fprintf(f, "\n");

  for (i = 0; i < numLayers(); i++) {
    layers_[i].print(f);
  }

  for (i = 0; i < numVias(); i++) {
    fprintf(f, "  Via %s\n", viaName(i));
  }
}

int lefiViaRule::numVias() const
{
  return numVias_;
}

char* lefiViaRule::viaName(int index) const
{
  if (index < 0 || index >= numVias_) {
    return nullptr;
  }
  return vias_[index];
}

int lefiViaRule::numProps() const
{
  return numProps_;
}

void lefiViaRule::addProp(const char* name, const char* value, const char type)
{
  int len = strlen(name) + 1;
  if (numProps_ == propsAllocated_) {
    int i;
    int max;
    int lim;
    char** nn;
    char** nv;
    double* nd;
    char* nt;

    if (propsAllocated_ == 0) {
      propsAllocated_ = 1;  // initialize propsAllocated_
    }
    max = propsAllocated_ *= 2;
    lim = numProps_;
    nn = (char**) lefMalloc(sizeof(char*) * max);
    nv = (char**) lefMalloc(sizeof(char*) * max);
    nd = (double*) lefMalloc(sizeof(double) * max);
    nt = (char*) lefMalloc(sizeof(char) * max);
    for (i = 0; i < lim; i++) {
      nn[i] = names_[i];
      nv[i] = values_[i];
      nd[i] = dvalues_[i];
      nt[i] = types_[i];
    }
    lefFree(names_);
    lefFree(values_);
    lefFree(dvalues_);
    lefFree(types_);
    names_ = nn;
    values_ = nv;
    dvalues_ = nd;
    types_ = nt;
  }
  names_[numProps_] = (char*) lefMalloc(sizeof(char) * len);
  strcpy(names_[numProps_], name);
  len = strlen(value) + 1;
  values_[numProps_] = (char*) lefMalloc(sizeof(char) * len);
  strcpy(values_[numProps_], value);
  dvalues_[numProps_] = 0;
  types_[numProps_] = type;
  numProps_ += 1;
}

void lefiViaRule::addNumProp(const char* name,
                             const double d,
                             const char* value,
                             const char type)
{
  int len = strlen(name) + 1;
  if (numProps_ == propsAllocated_) {
    int i;
    int max;
    int lim;
    char** nn;
    char** nv;
    double* nd;
    char* nt;

    if (propsAllocated_ == 0) {
      propsAllocated_ = 1;  // initialize propsAllocated_
    }
    max = propsAllocated_ *= 2;
    lim = numProps_;
    nn = (char**) lefMalloc(sizeof(char*) * max);
    nv = (char**) lefMalloc(sizeof(char*) * max);
    nd = (double*) lefMalloc(sizeof(double) * max);
    nt = (char*) lefMalloc(sizeof(char) * max);
    for (i = 0; i < lim; i++) {
      nn[i] = names_[i];
      nv[i] = values_[i];
      nd[i] = dvalues_[i];
      nt[i] = types_[i];
    }
    lefFree(names_);
    lefFree(values_);
    lefFree(dvalues_);
    lefFree(types_);
    names_ = nn;
    values_ = nv;
    dvalues_ = nd;
    types_ = nt;
  }
  names_[numProps_] = (char*) lefMalloc(sizeof(char) * len);
  strcpy(names_[numProps_], name);
  len = strlen(value) + 1;
  values_[numProps_] = (char*) lefMalloc(sizeof(char) * len);
  strcpy(values_[numProps_], value);
  dvalues_[numProps_] = d;
  types_[numProps_] = type;
  numProps_ += 1;
}

const char* lefiViaRule::propName(int i) const
{
  char msg[160];
  if (i < 0 || i >= numProps_) {
    sprintf(msg,
            "ERROR (LEFPARS-1431): The index number %d given for the VIARULE "
            "PROPERTY is invalid.\nValid index is from 0 to %d",
            i,
            numProps_);
    lefiError(0, 1431, msg);
    return nullptr;
  }
  return names_[i];
}

const char* lefiViaRule::propValue(int i) const
{
  char msg[160];
  if (i < 0 || i >= numProps_) {
    sprintf(msg,
            "ERROR (LEFPARS-1431): The index number %d given for the VIARULE "
            "PROPERTY is invalid.\nValid index is from 0 to %d",
            i,
            numProps_);
    lefiError(0, 1431, msg);
    return nullptr;
  }
  return values_[i];
}

double lefiViaRule::propNumber(int i) const
{
  char msg[160];
  if (i < 0 || i >= numProps_) {
    sprintf(msg,
            "ERROR (LEFPARS-1431): The index number %d given for the VIARULE "
            "PROPERTY is invalid.\nValid index is from 0 to %d",
            i,
            numProps_);
    lefiError(0, 1431, msg);
    return 0;
  }
  return dvalues_[i];
}

char lefiViaRule::propType(int i) const
{
  char msg[160];
  if (i < 0 || i >= numProps_) {
    sprintf(msg,
            "ERROR (LEFPARS-1431): The index number %d given for the VIARULE "
            "PROPERTY is invalid.\nValid index is from 0 to %d",
            i,
            numProps_);
    lefiError(0, 1431, msg);
    return 0;
  }
  return types_[i];
}

int lefiViaRule::propIsNumber(int i) const
{
  char msg[160];
  if (i < 0 || i >= numProps_) {
    sprintf(msg,
            "ERROR (LEFPARS-1431): The index number %d given for the VIARULE "
            "PROPERTY is invalid.\nValid index is from 0 to %d",
            i,
            numProps_);
    lefiError(0, 1431, msg);
    return 0;
  }
  return dvalues_[i] ? 1 : 0;
}

int lefiViaRule::propIsString(int i) const
{
  char msg[160];
  if (i < 0 || i >= numProps_) {
    sprintf(msg,
            "ERROR (LEFPARS-1431): The index number %d given for the VIARULE "
            "PROPERTY is invalid.\nValid index is from 0 to %d",
            i,
            numProps_);
    lefiError(0, 1431, msg);
    return 0;
  }
  return dvalues_[i] ? 0 : 1;
}
END_LEF_PARSER_NAMESPACE
